/**
Copyright 2011 Steven Moxley

This file is part of Joggle.

Joggle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Joggle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Joggle.  If not, see <http://www.gnu.org/licenses/>.
*/
package edu.gwu.cs.joggle;

import java.io.IOException;
import java.util.ArrayList;

import com.almworks.sqlite4java.SQLiteException;

/**
 * @author      Steven Moxley <steven.moxley@gmail.com>
 * @version     1                 
 * @since       2011-11-20
 */
public class Player extends User {

	protected Integer points;						// total accumulated points
	protected ArrayList<Integer> gamesPlayed;		// each int is a seed representing the game boards/states already played; can be used to prevent repeating games too often
	protected ArrayList<String> friends;			// list of approved friends
	protected Integer privacy;						// 0 makes profile public, 1 limits to friends only, and 2 means protected
	protected Boolean inGame;						// this player's current game (possibly null)
	protected Server server;						// where this account is valid
	protected Boolean newAccount;

    /**
     * @return Server associated with this player
     */
	protected Server getServer() {
        return server;
    }

    /**
     * @return Boolean Whether this Player is currently in a game
     */
	protected Boolean isInGame() {
    	inGame = server.isPlayerInGame(this);
    	return inGame;
    }

	/**
	 * @return Integer This Player's total accumulated points
	 */
	protected Integer getPoints() {
		// get updated points total from server
		points = server.getPlayerPoints(this);
		return points;
	}

	/**
	 * @param p Sets (overwrites) this Player's points
	 */
	protected void setPoints(Integer p) {
	    points = server.setPlayerPoints(this,p);
	}

	/**
	 * @return ArrayList<Integer> The Games played, as represented by int seed values
	 */
	protected ArrayList<Integer> getGamesPlayed() {
		String[] gamesPlayedAsStrings = server.getGamesPlayed(userName).split("#");
		for(String s : gamesPlayedAsStrings) {
			Integer gameSeed = new Integer(s);
			if(!gamesPlayed.contains(gameSeed)) {
				gamesPlayed.add(gameSeed);
			}
		}
		return gamesPlayed;
	}

    /**
     * @return String a String representation of the Games played
     */
	protected String getGamesPlayedAsString() {
		getGamesPlayed();
		String result = "";
		if(gamesPlayed.size() == 0) {
			return result;
		} else {
			for(Integer i : gamesPlayed) {
				result += i +"#";
			}
			return result;
		}
	}
	
	/**
	 * @param seed The Integer value used to seed the random number generator for the game board/state
	 */
	protected void addGamePlayed(Integer seed) {
		gamesPlayed.add(seed);
	}
	
	/**
	 * @param seed The Integer value used to seed the random number generator for the game board/state
	 */
	protected void removeGamePlayed(Integer seed) {
		gamesPlayed.remove(seed);
	}

	/**
	 * @return ArrayList<String> The ArrayList of Player's that are this Player's friend
	 */
	protected ArrayList<String> getFriends() {		
		friends = server.getPlayerFriends(this);
		friends.remove(userName);
		return friends;
	}

    /**
     * @return String a String representation of the Players that are this player's friend
     */
	protected String getFriendsAsString() {
		String result = "";
		if(friends.size() == 0) {
			return result;
		} else {
			
			for(String s : friends) {
				if(!s.equals("")) {
					result += s + "#";
				}
			}
			return result;
		}
	}
	
	/**
	 * @param p The Player to be added to this Player's ACL (for viewing profiles)
	 * @returns String a String Response
	 */
	protected String addFriend(Player p) {
		String result = "";
		if(p.equals(this) || p.getUserName().equals(userName)) { 
			result = "You cannot be your own friend!";
		} else {
			// confirm that this Player wants to add p as a friend
			try {
				server.makeFriends(p, this);
				friends.add(userName);
				result = "Successfully added " + friends.get(friends.size()-1) + " as a friend.";
			} catch (SQLiteException e) {
				e.printStackTrace();
				result = "Unable to add new friend";
			}
		}
		return result;
	}
	
	/**
	 * @param p The Player whose friendship is being queried
	 * @return boolean true if p is this Player's friend, false otherwise
	 */
	protected boolean isFriend(Player p) {
		getFriends();
		//System.out.println(getUserName() + " is friends with " + friends.get(friends.size()-1));
		return friends.contains(p.getUserName());
	}
	
	/**
	 * @param friend The Player this Player no longer wishes to be friends with (and so will be removed from this Player's ACL).  Before invoking, check that p.IsFriend() == true;
	 */
	protected void removeFriend(Player friend) {
		friends.remove(friend);
	}

	
	/**
	 * @return Integer This Player's privacy setting
	 */
	protected Integer getPrivacy() {
		//privacy = server.getPlayerPrivacy(this);
		return privacy;
	}

	
	/**
	 * @param p Sets (overwrites) this Player's privacy setting
	 */
	protected void setPrivacy(Integer p) {
		updateUserLog("updated privacy setting.\n");
		privacy = p;
	}
	
	
	/**
	 * @return Game Helper method for Player to create game with default settings (better to implement in Client?)
	 * @throws SQLiteException 
	 * @see Game
	 */
	protected Game createGame() throws SQLiteException {
		updateUserLog("created a game.\n");
		Game newGame = new Game(this);
		inGame = server.setPlayerInGame(this, true);
		return newGame;
	}
	
	/**
	 * @param minPlayers Minimum number of who need to join game before it can begin
	 * @param maxPlayers Maximum number of Players allowed to join this game
	 * @param minPoints Minimum skill level required to join this game
	 * @param maxPoints Maximum skill level allowed to join this game
	 * @param maxW Maximum number of seconds to wait for maxPlayers to join after minPlayers have joined before starting game
	 * @param duration Number of seconds the game will last
	 * @param seed For game board generation algorithm; good for auditing
	 * @param size Length and width of game board
	 * @param owner The Player who created this Game (this Player)
	 * @return Game Helper method for Player to create a game with specified options
	 * @see Game
	 */
	protected Game createGame(int minPlayers, int maxPlayers, int minPoints, int maxPoints, int maxW, int duration, int seed, int size, Player owner) {
		updateUserLog("created a game.\n");
		Dictionary dict = server.getAvailableDictionaries().get(0);
		Game newGame = new Game(minPlayers, maxPlayers, minPoints, maxPoints, maxW, duration, seed, size, dict, this, true);
		inGame = server.setPlayerInGame(this, true);
		return newGame;
	}
	
	// create a Game with a whitelist and blacklist of Players
	protected Game createGame(int minPlayers, int maxPlayers, int minPoints, int maxPoints, int maxW, int duration, int seed, int size, ArrayList<String> p, ArrayList<String> allowed, ArrayList<String> banned, Player owner) {
		updateUserLog("created a game.\n");
		Dictionary dict = server.getAvailableDictionaries().get(0);
		Game newGame = new Game(minPlayers, maxPlayers, minPoints, maxPoints, maxW, duration, seed, size, p, allowed, banned, dict, this, true);
		inGame = server.setPlayerInGame(this, true);
		return newGame;
	}	
	
	/**
	 * @param u Get the profile of this Player if u is authorized
	 */
	protected void getProfile(User u) {
		getPrivacy();
		/*
		 * specific conditions checked:
		 * is this Player's profile public (privacy = 0)?
		 * is u the owner of this Player?
		 * is u an Administrator?
		 * is u an authorized Auditor?
		 * is this Player's profile private (friends only, privacy = 1), and is u one of this Player's friends?
		*/
		if(privacy == 0 || userName.equals(u.getUserName()) || u instanceof Administrator || (u instanceof Auditor && ((Auditor) u).getAuthorizedPlayers().contains(this)) || (privacy == 1 && u instanceof Player && isFriend((Player) u))) {
			System.out.println(getFirstName() + " " + getLastName());
			System.out.println(getUserName() + " : " + getEmail());
			try {
				System.out.println(getUserName() + " has accumulated " + getPoints() + " points from " + gamesPlayed.size() + " games.");
			} catch (NullPointerException npe) {}
			System.out.println();
			
			getFriends();
			if(friends.contains("") || friends.size() == 0) {
				System.out.println(getUserName() + " has no friends.");
			} else {
				System.out.println(getUserName() + " has " + friends.size() + " friends:");
				for(String s : friends) {
					System.out.println(s);
				}
			}
			System.out.println();
			
			if(isInGame()) {
				System.out.println(userName + " is currently online and in a game.");
			}
		} else {
			System.out.println("Sorry, you are not authorized to view this user's profile.");
		}
	}
	
	/**
	 * @param g Game this Player wishes to join
	 * @throws Should eventually throw GameFullException
	 */
	protected void joinGame(Game g) {
		if(!inGame) {
			g.addPlayer(this);
			inGame = server.setPlayerInGame(this, true);
		} else {
			System.out.println("Cannot join game! You can only play one game at a time.");
		}
	}

	
	/** 
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return getFirstName() + " " + getLastName() + " (" + getUserName() + ")";
	}
	
	/**
	 * @param fName The Player's real first name
	 * @param lName The Player's real last name
	 * @param uName The Player's Joggle Playername
	 * @param e The Player's preferred email address
	 * @param p The Player's privacy setting
	 * @throws SQLiteException 
	 * @throws IOException 
	 */
	public Player(String fName, String lName, String uName, String email, String pwd, Integer p, Server s, Boolean newAcct) {
		super(fName, lName, uName, email, pwd);
		points = 0;
		friends = new ArrayList<String>();
		privacy = p;
		inGame = false;
		server = s;
		newAccount = newAcct;
		
		if(newAccount) {
			gamesPlayed = new ArrayList<Integer>();
			//System.out.println(pwd + " -> " + password);
			s.addPlayer(this);
		}
	}
	
}
